package network.server;

import graphics.Shape;
import graphics.ShapeFactory;

import java.net.Socket;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import network.Client;
import network.Message;
import network.MessageDispatcher;
import network.MessageFactory;
import network.MessageQueue;
import network.MessageReceiver;
import network.MessageType;
import controller.Constants;

/**
 * A client processer is a client request handler which is used to server the
 * client.
 * 
 * @author Jie Zhao(288654)
 * 
 */
public class ClientProcesser implements Runnable, Observer {

	private WhiteboardServer server = null;
	private Socket clientSocket = null;
	MessageQueue mq = new MessageQueue();
	Client c = new Client();
	MessageReceiver receiver = new MessageReceiver();

	public ClientProcesser(WhiteboardServer server, Socket client) {
		this.server = server;
		this.clientSocket = client;
		ClientList.getInstance().addObserver(this);
	}

	@Override
	public void run() {
		Message m = null;
		if (Constants.DEBUG)
			System.out.println("Porcessing client request:"
					+ clientSocket.getInetAddress());

		// c.setName(clientSocket.getInetAddress().toString());
		c.setClientSocket(clientSocket);
		try {
			m = receiver.receiveMessage(clientSocket);
			if (m.getType() == MessageType.LOGIN_REQ) {
				String login = m.getMessageBody();
				String[] temp = login.split(":");
				c.setName(temp[0]);
				String serverPassword = server.getPassword();
				if (serverPassword != null && !serverPassword.equals("")) {
					if (temp.length == 2 && temp[1] != null
							&& (!temp[1].equals(server.getPassword()))) {
						Message reject = MessageFactory.createLoginResp(false);
						MessageDispatcher.dispatch(clientSocket, reject);
						return;
					}
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
		new ClientHandler().start();
		new Processor().start();
		ClientList.getInstance().addClient(c);

	}

	class ClientHandler extends Thread {
		@Override
		public void run() {
			try {
				Message m = null;
				while (!clientSocket.isClosed()) {
					m = receiver.receiveMessage(clientSocket);
					mq.putMessage(m);
				}
			} catch (Exception e) {
				if (Constants.DEBUG)
					e.printStackTrace();
			}
		}

	}

	class Processor extends Thread {
		public Processor() {
		}

		@Override
		public void run() {
			while (true) {
				try {
					Message msg = mq.takeMessage();
					switch (msg.getType()) {
					case MessageType.CLEAR:
						ControlledShapesData.getInstance().clear();
						MessageDispatcher.dispatchToAll(msg);
						break;
					case MessageType.DRAW:
						Shape s = ShapeFactory.createShapeByString(msg
								.getMessageBody());
						s.fromString(msg.getMessageBody());
						ControlledShapesData.getInstance().addData(s);
						MessageDispatcher.dispatchToAll(msg);
						break;
					case MessageType.CHAT:
						MessageDispatcher.dispatchToAll(msg);
						break;
					case MessageType.DRAW_SYNC_REQ:
						Message m = MessageFactory.createDrawSyncMessage();
						try {
							MessageDispatcher.dispatch(clientSocket, m);
						} catch (Exception e) {
							if (Constants.DEBUG)
								e.printStackTrace();
						}
						break;
					default:
						break;
					}
					if (clientSocket.isClosed())
						break;
				} catch (Exception e) {
					if (Constants.DEBUG)
						e.printStackTrace();
				}
			}
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		if (this.clientSocket.isClosed() || !this.clientSocket.isConnected()
				|| !ClientList.getInstance().isInList(this.clientSocket)) {
			ClientList.getInstance().deleteObserver(this);
			return;
		}
		Message e = new Message();
		List<Client> cl = ClientList.getInstance().getClientList();
		StringBuffer list = new StringBuffer();
		for (Iterator<Client> i = cl.iterator(); i.hasNext();) {
			list.append(i.next().getName());
			if (i.hasNext())
				list.append(",");
		}
		e.setType(MessageType.USERNAME_SYNC);
		e.setMessageBody(list.toString());
		try {
			if (Constants.DEBUG)
				System.out.println("Writing user list to client...");
			clientSocket.getOutputStream().write(e.toByteArray());
			clientSocket.getOutputStream().flush();
		} catch (Exception ee) {
		}

	}
}
